using System;

namespace OhioState.AssetManaged
{
    /// <summary>
    /// Abstract class used for all objects supporting a publisher / subscriber
    /// use case.
    /// </summary>
    [Serializable()]
    public abstract class SharedAsset : MarshalByRefObject, IAssetManaged, IManipulator
    {
        #region Constructors
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="name">The string to use for the name of this object.</param>
        public SharedAsset(String name)
        {
            this.name = name;
            SetEventsHelper();
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        public SharedAsset() : this(String.Empty)
        {
        }
        #endregion

        /// <summary>
        /// Forces all of the camera events to fire, notifying all subscribers.
        /// </summary>
        public void UpdateClients()
        {
            FireUpdateChangedEvent(this);
            FireUpdateChangingEvent(this);
        }

        #region IAssetManaged
        /// <summary>
        /// Get or set the name of this object.
        /// </summary>
        public String Name
        {
            get { return name; }
            set { SetName(value); }
        }
        /// <summary>
        /// Set the name of this object and let subscribers know a change occurred.
        /// </summary>
        /// <param name="name">A string.</param>
        public virtual void SetName(string name)
        {
            this.name = name;
            FireUpdateChangedEvent(this);
        }
        /// <summary>
        /// Converts this instance to a string.
        /// </summary>
        /// <returns>A string representing the name field.</returns>
        public override string ToString()
        {
            return name;
        }

        //
        // Events for the drawable object
        //
        [field: NonSerializedAttribute()]
        private event IAssetManagedHandler UpdateChangingEvent;
        // Changed event will be called after a complete interaction or manipulation.
        [field: NonSerializedAttribute()]
        private event IAssetManagedHandler UpdateChangedEvent;
        /// <summary>
        /// Add a listener (or subscriber) to the ChangedEvent. Called everytime a <c>Manipulation</c>
        /// is completed.
        /// </summary>
        /// <param name="e">An <typeparamref name="IAssetManagedHandler"/> delegate.</param>
        /// <remarks>The difference between this method and <typeparamref name="AddChangingEventHandler"/>
        /// allows for a sequence of operations to occur before the manipulation is over and the
        /// ChangedEvent is called. For instance, mouse manipulations may occur continuously, but the 
        /// changed eveent will be fired once the manipulations are over (e.g. on the mouse up event.
        /// The ChangedEvent is typically fired with the EndManipulation of the <typeparamref name="IManipulation"/>
        /// interface.</remarks>
        /// <seealso cref="IManipulator"/>
        /// <seealso cref="RemoveChangedEventHandler"/>
        /// <seealso cref="AddChangingEventHandler"/>
        public void AddChangedEventHandler(IAssetManagedHandler e)
        {
            UpdateChangedEvent += e;
        }
        /// <summary>
        /// Remove a listener (or subscriber) to the ChangedEvent.
        /// </summary>
        /// <param name="e">An <typeparamref name="IAssetManagedHandler"/> delegate.</param>
        /// <seealso cref="AddChangedEventHandler"/>
        public void RemoveChangedEventHandler(IAssetManagedHandler e)
        {
            UpdateChangedEvent -= e;
        }
        /// <summary>
        /// Add an listener (or subscriber) to the ChangingEvent. Called everytime 
        /// any state associated with this type or derived types occurs.
        /// </summary>
        /// <param name="e">An <typeparamref name="IAssetManagedHandler"/> delegate.</param>
        /// <remarks>The listener should also subscribe to the ChangedEvent.</remarks>
        /// <seealso cref="IManipulator"/>
        /// <seealso cref="RemoveChangingEventHandler"/>
        /// <seealso cref="AddChangedEventHandler"/>
        public void AddChangingEventHandler(IAssetManagedHandler e)
        {
            UpdateChangingEvent += e;
        }
        /// <summary>
        /// Remove a listener (or subscriber) to the ChangingEvent.
        /// </summary>
        /// <param name="e">An <typeparamref name="IAssetManagedHandler"/> delegate.</param>
        /// <seealso cref="AddChangingEventHandler"/>
        public void RemoveChangingEventHandler(IAssetManagedHandler e)
        {
            UpdateChangingEvent -= e;
        }
        /// <summary>
        /// Interacts with the <typeparamref name="AssetManager"/> during serialization to reconnect to 
        /// existing assets in the AssetManager rather than duplicating them.
        /// </summary>
        public virtual void ReaquireAssets()
        {
        }
        #endregion

        #region IManipulator Members
        /// <summary>
        /// Prepares the manipulator for a sequence of actions.
        /// </summary>
        public virtual void BeginManipulation()
        {
            // Do nothing now.  Possibly could be used to keep track of the state
            //  which would be used for an Undo function
        }
        /// <summary>
        /// Signals the manipulator that the sequence of actions is finished.
        /// </summary>
        public virtual void EndManipulation()
        {
            // Fire event that tells clients that the object
            //  has been changed and that the manipulation is done.
            FireUpdateChangedEvent(this);
        }
        /// <summary>
        /// Cleans up any state in the manipulator.
        /// </summary>
        public virtual void Reset()
        {
            // Haven't seen a real need for this yet.  
        }
        #endregion

        #region Private and protected methods.
        /// <summary>
        /// Helper method to re-wire public properties of type IAssetManaged to
        /// insure this instance is subscribed to the correct instance.
        /// </summary>
        /// <param name="field">The old <typeparamref name="IAssetManaged"/> field of this class.</param>
        /// <param name="newValue">The new value to assign to the field.</param>
        /// <param name="observer">A <typeparamref name="IAssetManagedHandler"/> used in the Change Event.</param>
        protected void SetPropertyEventHandling( IAssetManaged field, IAssetManaged newValue, IAssetManagedHandler observer )
        {
            //
            // Remove changed event handler from old asset and add a new handler
            //  for the new asset
            //
            if (field != null)
            {
                field.RemoveChangedEventHandler(observer);
            }

            // check if the new value is null
            if (newValue != null)
            {
                //changedEvent = new IAssetManagedHandler(shader_ChangedEvent);
                newValue.AddChangedEventHandler(observer);
            }
        }

        /// <summary>
        /// Sets up the Events Helper class.
        /// </summary>
        /// <remarks>This class is a strategy so if we want new 
        /// functionality we only have to change one line.</remarks>
        protected void SetEventsHelper()
        {
            //_eventsHelper = new EventsStrategyOneWay();
            //_eventsHelper = new EventsHelper.EventsStrategyAsync();
            eventsHelper = new EventsStrategySerial();
        }
        /// <summary>
        /// Fires the update changing event.  This is in its own function so that we
        ///  can perform a little error checking.  The error checking that needs done
        ///  is to make sure that events helper is instantiated.  If this object
        ///  was loaded from a file using serialization the events helper will be null.
        /// </summary>
        /// <param name="asset">The <typeparamref name="IAssetManaged"/> instance to pass to the
        /// <typeparamref name="IAssetManagedHandler"/> subscribed to the event.</param>
        protected void FireUpdateChangingEvent(IAssetManaged asset)
        {
            if (eventsHelper == null)
                SetEventsHelper();
            eventsHelper.Fire(UpdateChangingEvent, this);
        }
        /// <summary>
        /// Fires the update changed event.  This is in its own function so that we
        ///  can perform a little error checking.  The error checking that needs done
        ///  is to make sure that events helper is instantiated.  If this object
        ///  was loaded from a file using serialization the events helper will be null.
        /// </summary>
        /// <param name="asset">The <typeparamref name="IAssetManaged"/> instance to pass to the
        /// <typeparamref name="IAssetManagedHandler"/> subscribed to the event.</param>
        protected void FireUpdateChangedEvent(IAssetManaged asset)
        {
            if (eventsHelper == null)
                SetEventsHelper();
            eventsHelper.Fire(UpdateChangedEvent, this);
        }
        #endregion

        #region Private and protected fields
        /// <summary>
        /// A string representing the name used to identify this asset.
        /// </summary>
        protected String name;
        /// <summary>
        /// Event helper for firing events.
        /// </summary>
        [NonSerialized()]
        protected EventsStrategyAbstract eventsHelper;
        #endregion
    }
}
